/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Includes
#include "SkugoWeakReference.hpp"
#include <stddef.h>

namespace Skugo
{
  // Constructor
  WeakBase::WeakBase(void* object) :
    mObject(object),
    mNext(NULL),
    mPrev(NULL),
    mLockCount(0)
  {
  }

  // Destructor
  WeakBase::~WeakBase()
  {
    // Unlink itself
    Clear();
  }

  // Check if the weak pointer is valid
  bool WeakBase::IsValid()
  {
    return mObject != NULL;
  }

  // Lock the node (returns true if the pointer is still valid)
  // Typically this is used in an if conditional
  bool WeakBase::Lock()
  {
    // If the reference is still valid...
    if (IsValid())
    {
      // Increment the lock count and return a success
      ++mLockCount;
      return true;
    }

    // Otherwise, the pointer is NULL, the lock failed
    return false;
  }

  // Unlock the node (it is an error to not match up a lock with an unlock)
  // Typically this is used at the end of an if statement afer a lock
  void WeakBase::Unlock()
  {
    // It's an error if the lock count does not match up
    SkugoErrorIf(mLockCount == 0, "Mismatched Lock and Unlock on a weak reference");

    // Decrement the lock count
    --mLockCount;
  }

  // Clear the pointer
  void WeakBase::Clear()
  {
    // If it's somehow being destroyed, yet it's locked, that's a serious error
    SkugoErrorIf(mLockCount != 0, "Attempting to destroy a locked weak reference");

    // Unlink from the linked list
    Unlink();

    // Null out the object pointer
    mObject = NULL;
  }

  // Disconnect from the list
  void WeakBase::Unlink()
  {
    // If the object is already NULL (eg we are already unlinked)...
    if (mObject == NULL)
    {
      // Error checking
      SkugoErrorIf(this->mPrev != NULL && this->mPrev != this, "The object has no value yet the prev is pointing at something");
      SkugoErrorIf(this->mNext != NULL && this->mNext != this, "The object has no value yet the next is pointing at something");

      // Skip this portion
      return;
    }

    // Error checking
    SkugoErrorIf(this->mPrev->mNext != this, "My previous's next should always be me");
    SkugoErrorIf(this->mNext->mPrev != this, "My next's previous should always be me");

    // My previous's next is my next
    this->mPrev->mNext = this->mNext;

    // My next's previous is my previous
    this->mNext->mPrev = this->mPrev;

    // Clear out the next and previous pointers
    this->mNext = NULL;
    this->mPrev = NULL;
  }

  // Insert this object after a given link
  void WeakBase::InsertAfter(WeakBase* previous)
  {
    // My previous is the given node
    this->mPrev = previous;

    // My next is the given node's next
    this->mNext = previous->mNext;

    // My next's previous is now me
    this->mNext->mPrev = this;

    // My previou's next is now me
    this->mPrev->mNext = this;
  }

  // Constructor
  LifetimeTracker::LifetimeTracker() :
    WeakBase(NULL)
  {
    // The sentenal always wraps around to itself
    mNext = this;
    mPrev = this;
  }

  // Destructor
  LifetimeTracker::~LifetimeTracker()
  {
    // Iterate through all the links and clear them out
    // Basically, since we unlink each node, we are technically modifying a container
    // while iterating through it, but we know this, hence the weird looping

    // Loop until we circle back around to ourself
    while (mNext != this)
    {
      // Clear out the next node
      mNext->Clear();
    }
  }
}
